home *** CD-ROM | disk | FTP | other *** search
Java Source | 1998-09-28 | 10.8 KB | 387 lines |
- import com.sun.java.swing.*;
- import com.sun.java.swing.table.*;
- import com.sun.java.swing.event.*;
-
- import java.awt.event.*;
- import java.io.*;
- import java.beans.*;
- import java.util.*;
-
- public class SimpleSortTableModel extends com.sun.java.swing.table.AbstractTableModel implements java.io.Serializable, TableModelListener
- {
- TableModel model=null;
-
- int[] indexes = {};
- Vector sortingColumns = new Vector();
- boolean ascending=true;
- int compares;
-
- public SimpleSortTableModel()
- {
- }
-
- // There is no-where else to put this.
- // Add a mouse listener to the Table to trigger a table sort
- // when a column heading is clicked in the JTable.
- public void addMouseListenerToHeaderInTable(JTable table)
- {
- final SimpleSortTableModel sorter = this;
- final JTable tableView = table;
- tableView.setColumnSelectionAllowed(false);
- MouseAdapter listMouseListener = new MouseAdapter()
- {
- public void mouseClicked(MouseEvent e)
- {
- TableColumnModel columnModel = tableView.getColumnModel();
- int viewColumn = columnModel.getColumnIndexAtX(e.getX());
- int column = tableView.convertColumnIndexToModel(viewColumn);
- if(e.getClickCount() == 1 && column != -1)
- {
- System.out.println("Sorting ...");
- int shiftPressed = e.getModifiers()&InputEvent.SHIFT_MASK;
- boolean ascending = (shiftPressed == 0);
- sorter.sortByColumn(column, ascending);
- }
- }
- };
- JTableHeader th = tableView.getTableHeader();
- th.addMouseListener(listMouseListener);
- }
-
- public void reallocateIndexes()
- {
- int rowCount = model.getRowCount();
-
- // Set up a new array of indexes with the right number of elements
- // for the new data model.
- indexes = new int[rowCount];
-
- // Initialise with the identity mapping.
- for(int row = 0; row < rowCount; row++)
- {
- indexes[row] = row;
- }
- }
-
- public int compareRowsByColumn(int row1, int row2, int column)
- {
- Class type = model.getColumnClass(column);
- TableModel data = model;
-
- // Check for nulls
-
- Object o1 = data.getValueAt(row1, column);
- Object o2 = data.getValueAt(row2, column);
-
- // If both values are null return 0
- if (o1 == null && o2 == null)
- {
- return 0;
- }
- else if (o1 == null)
- { // Define null less than everything.
- return -1;
- }
- else if (o2 == null)
- {
- return 1;
- }
-
- /* We copy all returned values from the getValue call in case
- an optimised model is reusing one object to return many values.
- The Number subclasses in the JDK are immutable and so will not be used in
- this way but other subclasses of Number might want to do this to save
- space and avoid unnecessary heap allocation.
- */
- if (type.getSuperclass() == java.lang.Number.class)
- {
- Number n1 = (Number)data.getValueAt(row1, column);
- double d1 = n1.doubleValue();
- Number n2 = (Number)data.getValueAt(row2, column);
- double d2 = n2.doubleValue();
-
- if (d1 < d2)
- {
- return -1;
- }
- else if (d1 > d2)
- {
- return 1;
- }
- else
- {
- return 0;
- }
- }
- else if (type == java.util.Date.class)
- {
- Date d1 = (Date)data.getValueAt(row1, column);
- long n1 = d1.getTime();
- Date d2 = (Date)data.getValueAt(row2, column);
- long n2 = d2.getTime();
-
- if (n1 < n2)
- {
- return -1;
- }
- else if (n1 > n2)
- {
- return 1;
- }
- else
- {
- return 0;
- }
- }
- else if (type == String.class)
- {
- String s1 = (String)data.getValueAt(row1, column);
- String s2 = (String)data.getValueAt(row2, column);
- int result = s1.compareTo(s2);
-
- if (result < 0)
- {
- return -1;
- }
- else if (result > 0)
- {
- return 1;
- }
- else
- {
- return 0;
- }
- }
- else if (type == Boolean.class)
- {
- Boolean bool1 = (Boolean)data.getValueAt(row1, column);
- boolean b1 = bool1.booleanValue();
- Boolean bool2 = (Boolean)data.getValueAt(row2, column);
- boolean b2 = bool2.booleanValue();
-
- if (b1 == b2)
- {
- return 0;
- }
- else if (b1) // Define false < true
- {
- return 1;
- }
- else
- {
- return -1;
- }
- }
- else
- {
- Object v1 = data.getValueAt(row1, column);
- String s1 = v1.toString();
- Object v2 = data.getValueAt(row2, column);
- String s2 = v2.toString();
- int result = s1.compareTo(s2);
-
- if (result < 0)
- {
- return -1;
- }
- else if (result > 0)
- {
- return 1;
- }
- else
- {
- return 0;
- }
- }
- }
-
- public int compare(int row1, int row2)
- {
- compares++;
- for(int level = 0; level < sortingColumns.size(); level++)
- {
- Integer column = (Integer)sortingColumns.elementAt(level);
- int result = compareRowsByColumn(row1, row2, column.intValue());
- if (result != 0)
- {
- return ascending ? result : -result;
- }
- }
- return 0;
- }
-
- public void checkModel()
- {
- if (indexes.length != model.getRowCount())
- {
- System.err.println("Sorter not informed of a change in model.");
- }
- }
-
- public void sort(Object sender)
- {
- checkModel();
-
- compares = 0;
- // n2sort();
- // qsort(0, indexes.length-1);
- shuttlesort((int[])indexes.clone(), indexes, 0, indexes.length);
- System.out.println("Compares: "+compares);
- }
- public void n2sort()
- {
- for(int i = 0; i < getRowCount(); i++)
- {
- for(int j = i+1; j < getRowCount(); j++)
- {
- if (compare(indexes[i], indexes[j]) == -1)
- {
- swap(i, j);
- }
- }
- }
- }
-
- // This is a home-grown implementation which we have not had time
- // to research - it may perform poorly in some circumstances. It
- // requires twice the space of an in-place algorithm and makes
- // NlogN assigments shuttling the values between the two
- // arrays. The number of compares appears to vary between N-1 and
- // NlogN depending on the initial order but the main reason for
- // using it here is that, unlike qsort, it is stable.
- public void shuttlesort(int from[], int to[], int low, int high)
- {
- if (high - low < 2)
- {
- return;
- }
- int middle = (low + high)/2;
- shuttlesort(to, from, low, middle);
- shuttlesort(to, from, middle, high);
-
- int p = low;
- int q = middle;
-
- /* This is an optional short-cut; at each recursive call,
- check to see if the elements in this subset are already
- ordered. If so, no further comparisons are needed; the
- sub-array can just be copied. The array must be copied rather
- than assigned otherwise sister calls in the recursion might
- get out of sinc. When the number of elements is three they
- are partitioned so that the first set, [low, mid), has one
- element and and the second, [mid, high), has two. We skip the
- optimisation when the number of elements is three or less as
- the first compare in the normal merge will produce the same
- sequence of steps. This optimization seems to be worthwhile
- for partially ordered lists but some analysis is needed to
- find out how the performance drops to Nlog(N) as the initial
- order diminishes - it may drop very quickly. */
-
- if (high - low >= 4 && compare(from[middle-1], from[middle]) <= 0)
- {
- for (int i = low; i < high; i++)
- {
- to[i] = from[i];
- }
- return;
- }
-
- // A normal merge.
-
- for(int i = low; i < high; i++)
- {
- if (q >= high || (p < middle && compare(from[p], from[q]) <= 0))
- {
- to[i] = from[p++];
- }
- else
- {
- to[i] = from[q++];
- }
- }
- }
-
- public void swap(int i, int j)
- {
- int tmp = indexes[i];
- indexes[i] = indexes[j];
- indexes[j] = tmp;
- }
-
- public void sortByColumn(int column)
- {
- sortByColumn(column, true);
- }
-
- public void sortByColumn(int column, boolean ascending)
- {
- this.ascending = ascending;
- sortingColumns.removeAllElements();
- sortingColumns.addElement(new Integer(column));
- sort(this);
- fireTableChanged(new TableModelEvent(this));
- }
-
- //Properties:
- public void setModel(TableModel tm)
- {
- if (model!=null)
- {
- model.removeTableModelListener(this);
- }
- model = tm;
- model.addTableModelListener(this);
-
- reallocateIndexes();
- }
- public TableModel getModel()
- {
- return model;
- }
-
- //TableModel methods:
- public Object getValueAt(int row, int col)
- {
- checkModel();
- return model.getValueAt(indexes[row], col);
- }
- public void setValueAt(Object value, int row, int col)
- {
- checkModel();
- model.setValueAt(value,indexes[row],col);
- }
-
- public int getRowCount()
- {
- return model==null ? 0 : model.getRowCount();
- }
-
- public int getColumnCount()
- {
- return model==null ? 0 : model.getColumnCount();
- }
-
- public String getColumnName(int col)
- {
- return model.getColumnName(col);
- }
-
- public Class getColumnClass(int col)
- {
- return model.getColumnClass(col);
- }
-
- public boolean isCellEditable(int row, int col)
- {
- return model.isCellEditable(indexes[row], col);
- }
-
- // Implementation of the TableModelListener interface,
-
- public void tableChanged(TableModelEvent e)
- {
- System.out.println("SimpleSortTableModel:table changed");
- reallocateIndexes();
- fireTableChanged(e);
- }
- }